/* ====================================================================
 * (#)Header: DaoJpa.java
 * (#)Revision: 
 * (#)Date Created: Mar 3, 2007 2:27:06 AM
 * (#)Created By: Karol Wozniak
 * ====================================================================
 *
 * Copyright (c) 2007 karolak .  All rights reserved.
 *
 * ====================================================================
 * 
 * The software application represented here is the sole property of 
 * karolak. Its unauthorized utilization is expressly prohibited.
 * 
 * ====================================================================
 */
package com.karolak.dao.jpa;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.karolak.dao.Dao;
import com.karolak.dao.jpa.query.QueryBuilderJpa;
import com.karolak.query.Criterias;

/**
 * <code>DaoJpa</code> is used to ....
 * 
 * @author <a href="mailto:kwozniak@hotmail.com">Karol Wozniak</a>
 */
public class DaoJpa implements Dao {

	protected final Log log = LogFactory.getLog(getClass());

	@PersistenceContext(unitName = "entity")
	private EntityManager entityManager;

	private QueryBuilderJpa queryBuilder;

	public DaoJpa() {
		this.queryBuilder = QueryBuilderJpa.getInstance();
	}

	// === finders === //
	public <T, Id> T findById(Class<T> clazz, Id id) {
		return getEntityManager().find(clazz, id);
	}

	@SuppressWarnings("unchecked")
	public <T> T find(Class<T> clazz, Criterias criterias) {
		String ql = getQueryBuilder().createSelectClause(clazz, criterias);
		Query query = getEntityManager().createQuery(ql);
		getQueryBuilder().setNamedParameters(query, criterias);
		T entity = null;
		try {
			entity = (T) query.getSingleResult();
		} catch (NoResultException e) {
			//
		}
		return entity;
	}

	@SuppressWarnings(value = { "unchecked" })
	public <T> T find(String queryName, Map<String, Object> paramMap) {
		Query query = getEntityManager().createNamedQuery(queryName);
		getQueryBuilder().setNamedParameters(query, paramMap);
		T entity = null;
		try {
			entity = (T) query.getSingleResult();
		} catch (NoResultException e) {
			//
		}
		return entity;
	}

	// === crud == //
	public <T> T persist(T entity) {
		getEntityManager().persist(entity);
		getEntityManager().flush();
		return entity;
	}

	public <T> T merge(T entity) {
		entity = getEntityManager().merge(entity);
		getEntityManager().flush();
		return entity;
	}

	public <T> void remove(T entity) {
		getEntityManager().remove(entity);
	}

	public <T, Id> void remove(Class<T> clazz, Id id) {
		T entity = findById(clazz, id);
		getEntityManager().remove(entity);
	}

	// === selectors === //
	@SuppressWarnings("unchecked")
	public <T> List<T> select(Class<T> clazz) {
		String ql = getQueryBuilder().createSelectClause(clazz);
		Query query = getEntityManager().createQuery(ql);
		List<T> resultList = query.getResultList();
		return resultList;
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> select(Class<T> clazz, Criterias criterias) {
		String ql = getQueryBuilder().createSelectClause(clazz, criterias);
		Query query = getEntityManager().createQuery(ql);
		getQueryBuilder().setNamedParameters(query, criterias);
		List<T> resultList = query.getResultList();
		return resultList;
	}

	@SuppressWarnings(value = { "unchecked" })
	public <T> List<T> select(String queryName, Map<String, Object> paramMap) {
		Query query = getEntityManager().createNamedQuery(queryName);
		getQueryBuilder().setNamedParameters(query, paramMap);
		List<T> resultList = query.getResultList();
		return resultList;
	}

	// === range selectors === //
	@SuppressWarnings("unchecked")
	public <T> List<T> select(Class<T> clazz, int startPosition, int maxResult) {
		String ql = getQueryBuilder().createSelectClause(clazz);
		Query query = getEntityManager().createQuery(ql);
		query.setFirstResult(startPosition);
		query.setMaxResults(maxResult);
		List<T> resultList = query.getResultList();
		return resultList;
	}

	@SuppressWarnings(value = { "unchecked" })
	public <T> List<T> select(Class<T> clazz, Criterias criterias, int startPosition, int maxResult) {
		String ql = getQueryBuilder().createSelectClause(clazz, criterias);
		Query query = getEntityManager().createQuery(ql);
		getQueryBuilder().setNamedParameters(query, criterias);
		query.setFirstResult(startPosition);
		query.setMaxResults(maxResult);
		List<T> resultList = query.getResultList();
		return resultList;
	}

	@SuppressWarnings(value = { "unchecked" })
	public <T> List<T> select(String queryName, Map<String, Object> paramMap, int startPosition, int maxResult) {
		Query query = getEntityManager().createNamedQuery(queryName);
		getQueryBuilder().setNamedParameters(query, paramMap);
		query.setFirstResult(startPosition);
		query.setMaxResults(maxResult);
		List<T> resultList = query.getResultList();
		return resultList;
	}

	// === count selectors === //
	public <T> int selectCount(Class<T> clazz) {
		String ql = getQueryBuilder().createSelectCountClause(clazz);
		Query query = getEntityManager().createQuery(ql);
		return getCount(query);
	}

	public <T> int selectCount(Class<T> clazz, Criterias criterias) {
		String ql = getQueryBuilder().createSelectCountClause(clazz, criterias);
		Query query = getEntityManager().createQuery(ql);
		getQueryBuilder().setNamedParameters(query, criterias);
		return getCount(query);
	}

	public int selectCount(String queryName, Map<String, Object> paramMap) {
		Query query = getEntityManager().createNamedQuery(queryName);
		getQueryBuilder().setNamedParameters(query, paramMap);
		return getCount(query);
	}

	// === utility api === //
	protected int getCount(Query query) {
		Object o = query.getSingleResult();
		int value = ((Long) o).intValue();
		return value;
	}

	// === get/set === //
	public EntityManager getEntityManager() {
		return entityManager;
	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	public QueryBuilderJpa getQueryBuilder() {
		return queryBuilder;
	}
}
